package com.ai.opc;

import cn.hutool.core.util.ObjectUtil;
import com.ai.common.constant.OpcConstant;
import com.ai.entity.made.OpcServerInfo;
import com.ai.service.made.IOpcServerInfoService;
import lombok.extern.slf4j.Slf4j;
import org.eclipse.milo.opcua.sdk.client.OpcUaClient;
import org.eclipse.milo.opcua.sdk.client.api.UaClient;
import org.eclipse.milo.opcua.sdk.client.api.UaSession;
import org.eclipse.milo.opcua.sdk.client.api.identity.AnonymousProvider;
import org.eclipse.milo.opcua.sdk.client.api.identity.UsernameProvider;
import org.eclipse.milo.opcua.sdk.client.api.subscriptions.UaSubscription;
import org.eclipse.milo.opcua.stack.core.UaException;
import org.eclipse.milo.opcua.stack.core.security.SecurityPolicy;
import org.eclipse.milo.opcua.stack.core.types.builtin.LocalizedText;
import org.eclipse.milo.opcua.stack.core.types.builtin.unsigned.UInteger;

import java.io.IOException;
import java.nio.file.Files;
import java.nio.file.Path;
import java.nio.file.Paths;

import static com.ai.common.constant.OpcConstant.OPC_SERVER_ID_CLIENT_MAP;

/**
 * @Author jiaok
 * @Date 2024-11-14 10:23
 */
@Slf4j
public class OpcServerRunnable  implements Runnable{


    /**
     * 需要连接的opcServer
     */
    private OpcServerInfo opcServerInfo;

    private IOpcServerInfoService iOpcServerInfoService;

    public OpcServerRunnable(OpcServerInfo opcServerInfo, IOpcServerInfoService iOpcServerInfoService) {
        this.opcServerInfo = opcServerInfo;
        this.iOpcServerInfoService = iOpcServerInfoService;
    }

    /**
     * 匿名连接
     * @param endPointUrl opc端点地址
     * @return   opc客户端连接
     * @throws Exception 连接异常
     */
    private OpcUaClient createAnonymousClient(String endPointUrl) throws Exception {
            Path securityTempDir = Paths.get(System.getProperty("java.io.tmpdir"), "security");
            Files.createDirectories(securityTempDir);
            if (!Files.exists(securityTempDir)) {
                throw new Exception("unable to create security dir: " + securityTempDir);
            }
            return OpcUaClient.create(endPointUrl,
                    endpoints ->
                            endpoints.stream()
                                    .filter(e -> e.getSecurityPolicyUri().equals(SecurityPolicy.None.getUri()))
                                    .findFirst(),
                    configBuilder ->
                            configBuilder
                                    .setApplicationName(LocalizedText.english(""))
                                    .setApplicationUri("")
                                    //访问方式 new AnonymousProvider()
//                                    .setIdentityProvider(new UsernameProvider("ua_test", "123456"))
                                    .setIdentityProvider(new AnonymousProvider())
                                    .setRequestTimeout(UInteger.valueOf(5000))
                                    .build()
            );
    }

    /**
     * 密钥连接
     * @param endPointUrl opc端点地址
     * @return   opc客户端连接
     * @throws Exception 连接异常
     */
    private OpcUaClient createSecretClient(String endPointUrl) throws Exception {
            Path securityTempDir = Paths.get(System.getProperty("java.io.tmpdir"), "security");
            Files.createDirectories(securityTempDir);
            if (!Files.exists(securityTempDir)) {
                throw new Exception("unable to create security dir: " + securityTempDir);
            }
            return OpcUaClient.create(endPointUrl,
                    endpoints ->
                            endpoints.stream()
                                    .filter(e -> e.getSecurityPolicyUri().equals(SecurityPolicy.None.getUri()))
                                    .findFirst(),
                    configBuilder ->
                            configBuilder
                                    .setApplicationName(LocalizedText.english(""))
                                    .setApplicationUri("")
                                    //访问方式 new AnonymousProvider()
                                    .setIdentityProvider(new UsernameProvider(this.opcServerInfo.getAccount(), this.opcServerInfo.getPassword()))
//                                    .setIdentityProvider(new AnonymousProvider())
                                    .setRequestTimeout(UInteger.valueOf(5000))
                                    .build()
            );
    }


    @Override
    public void run() {
        String endPointUrl = "opc.tcp://"+this.opcServerInfo.getIp()+":"+this.opcServerInfo.getPort();
        try {

            OpcUaClient opcClient ;
            if (this.opcServerInfo.getConnectMode().equals(OpcConstant.OPC_CONNECT_ANONYMOUS)) {
                //匿名登录
                opcClient = this.createAnonymousClient(endPointUrl);

            }else {
                //密钥登录
                opcClient = this.createSecretClient(endPointUrl);
            }

            log.info("=======================   OOO     PPPPP    CCCC  ==========================");
            log.info("=======================  O   O    P    P   C      ===========================");
            log.info("=======================  O   O    PPPPP    C     ===========================");
            log.info("=======================  O   O    P        C      ===========================");
            log.info("=====================     OOO     P         CCCC   ===========================");

            log.info("===========================  OPC连接成功 :{} ===========================",endPointUrl);

            //修改opcServerInfo的连接状态
            iOpcServerInfoService.updateOpcServerState(this.opcServerInfo.getId(),"1");
            //建立连接
            UaClient uaClient = opcClient.connect().get();
            if (!ObjectUtil.isEmpty(uaClient)){
                OpcConstant.OPC_SERVER_ID_CLIENT_MAP.put(this.opcServerInfo.getId(),uaClient);
//                UaSubscription uaSubscription = uaClient.getSubscriptionManager().createSubscription(1000.0).get();
//                OpcConstant.OPC_CLIENT_SUBSCRIBE_MAP.put(this.opcServerInfo.getId(),uaSubscription);
            }

        }catch (Exception e){
            log.error("=======================   OOO     PPPPP    CCCC  ==========================");
            log.error("=======================  O   O    P    P   C      ===========================");
            log.error("=======================  O   O    PPPPP    C     ===========================");
            log.error("=======================  O   O    P        C      ===========================");
            log.error("=====================     OOO     P         CCCC   ===========================");

            log.error("===========================  OPC连接失败 :{} ===========================",endPointUrl);

            //修改opcServerInfo的连接状态
            iOpcServerInfoService.updateOpcServerState(this.opcServerInfo.getId(),"-1");
            // TODO: 2024-11-14 连接异常，后期补偿通过定时任务？


            e.printStackTrace();
        }
    }
}
